Linux 内存调优之如何限制进程、系统级别内存资源
我看远山,远山悲悯
写在前面
- 博文内容涉及 通过
Cgroup ,ulimit,内核参数
等限制进程、系统级别内存资源 - 理解不足小伙伴帮忙指正 :),生活加油
我看远山,远山悲悯
持续分享技术干货,感兴趣小伙伴可以关注下 ^_^
限制内存使用量
今天和小伙伴分享一些Linux 内存限制
相关知识,主要涉及如何配置以及什么情况下需要配置
,我们知道内存属于不可压缩资源
,当没有那么多的物理内存可以映射,进程都无法启动,所以为了公平,亦或是考虑部分进程 Qos 级别,一般情况下会对进程进行内存限制
,即保证机器上的多个进程不会因为业务对基础资源的弹性要求,相互影响
,比如类似FTP进程
的内存泄露
问题影响到核心业务服务触发 OOM
,甚至直接被OOM killer
掉。
简单介绍,关于内存资源限制在 Linux
中,一般按照限制手段
来划分的话,分为
- 内核参数(包括启动配置)限制: 临时修改交换分区频率
sysctl -w vm.swappiness=10
, 启动引导配置大页参数grubby --update-kernel=ALL --args="hugepagesz=1G hugepages=10"
- Cgroup(包括systemd)限制: 通过 Cgroup 的 memory 子系统限制
/sys/fs/cgroup/memory/myapp/memory.limit_in_bytes
,MemoryMax=1G
- ulimit 限制:
ulimit -v 2097152
三种方式,按照限制类型
划分,一般分为
- 系统内存限制: 比如修改内核参数
sysctl -w vm.overcommit_memory=2
禁止过度分配虚拟内存 - 进程内存限制: Systemd 单元限制进程物理内存不超过 1G
MemoryMax=1G
- 用户会话内存限制:
echo "john hard as 2097152" >> /etc/security/limits.conf
限制用户 john 的进程最大虚拟内存(地址空间)为 2 GB
如果按照具体的内存种类
划分,可以分为:
物理、虚拟
内存限制: 硬限制物理内存大小memory.limit_in_bytes
, 会话级别虚拟内存限制ulimit -v
- 数据段,数据栈限制:
ulimit -d 262144
设置数据段的最大值 - 数据缓存区: 释放内存缓存区设置
vm.drop_caches
,网络IO 相关缓存区配置net.ipv4.tcp_rmem
- 大页,脏页相关内存页限制:大页大小
vm.nr_hugepages
Cgroup
Cgroup(Control Groups)
最早由 google 开发,后来内置到了 Linux 内核中,是Linux kernel(Linux内核)
的一项功能,目前是很多虚拟化容器技术的底层核心技术。
在一个系统中运行的层级制进程组
,Cgroup
可对其进行资源分配(如CPU时间、系统内存、网络带宽或者这些资源的组合)
。
通过使用cgroup
,系统管理员在分配、排序、拒绝、管理和监控系统资源等方面,可以进行精细化控制。硬件资源可以在应用程序和用户间智能分配,从而增加整体效率。
Cgroup
可对进程
进行层级式分组并标记
,并对其可用资源进行限制
。通过将cgroup
层级系统与systemd
单位树捆绑, Linux 可以把资源管理设置从进程级别
移至应用程序
级别。
可以使用systemctl指令,或者通过修改systemd单位文件来管理系统资源。
为了控制重要的内核资源,systemd
会自动挂载/sys/fs/cgroup
目录实现 cgroup
分层架构,Linux 内核的资源管理器,也叫 CGroup 子系统,代表某一种单一资源(如 CPU 时间或内存等
Linux 内核提供了一系列资源管理器,由 systemd
自动挂载。如果需要查看已经挂载的资源管理器列表,可以参考/proc/cgroups
内存子系统位于其中:
memory
: 对 cgroup 中的任务使用内存量进行限制,并且自动生成任务占用内存资源的报告
在安装了 kernel-doc 软件包后,可以在/usr/share/doc/kernel-doc-<version>/Documentation/cgroup
目录下找相关管理器的说明文档,从而配置合适的资源限制
1 | ┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] |
简单的信息可以通过索引文件的了解
1 | ┌──[root@liruilongs.github.io]-[/usr/share/doc/kernel-doc-4.18.0/Documentation/cgroup-v1] |
Cgroup 限制内存资源常见的有两种,一种是 通过 systemd,一种是直接通过 cgroup 文件系统进行配置,这里我们先介绍通过 systemd
的方式配置,因为这种方式比较简单,而且可以做到进程级别
的配置,而通过 cgroup
的方式,可以做到系统级别
的配置,前提是当前Linux 机器使用 systemd,并且所有启动进程纳管到了 cgroup 子树,那么可以通过限制顶层树的资源限制,来实现整个系统的资源限制。
在 systemd
的 drop-in
文件文件[Service]
段里面定义 MemoryLimit
值就可以限制你的程序所使用的内存,单位可以是K,M,G或T。
这里看一个以临时服务的方式运行 /bin/bash
命令的Demo ,对 内存和CPU 进行限制, 并将其标准输入、标准输出、标准错误连接到当前的 TTY 设备上:
1 | ┌──[root@liruilongs.github.io]-[~] |
在生成的 bash Service
中我们可以运行交互命令,查看当前 Service 的单元文件,MemoryLimit=5242880
,即限制内存使用量 5M
1 | ┌──[root@liruilongs.github.io]-[/] |
通过 systemctl status bash-limit.service
我们可以看到cgroup
的相关信息
1 | ┌──[root@liruilongs.github.io]-[/] |
bash-limit.service 这个 service 的上级子树为 bash.slice 这个分组。
1 | Memory: 1.7M (limit: 5.0M) |
当然上面的配置方式实际上是基于 Cgroup
来实现的,Cgroup V1 版本和 V2 版本有些区别,当前机器环境的问题,我们只看一下 V1 的版本,MemoryLimit 参数可以控制Cgroup 内存控制器的 memory.limit_in_bytes Cgroup参数
。
对于运行中的 service 可以直接通过set-property 的方式来修改
1 | # 如需使用命令列来限定 httpd.service 的 CPU 和内存占用量,请输入: |
下面为 system.slice
这个 Cgroup
分组下面 tuned Cgroup
内存相关资源限制,可以看到默认的情况下没有限制(memory.limit_in_bytes
),使用的最大值,这里的内存限制是物理内存,不是虚拟内存,
tuned 小伙伴们应该不陌生,一个开源的系统性能优化的服务,用于一些内核参数限制
1 | ┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/memory/system.slice] |
默认情况下,没有限制会显示最大值
1 | ┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/memory/system.slice] |
配置方式我们可以通过上面的方式配置,通过 service unit 文件进行限制
限定最大可用内存为 1GB,添加 MemoryLimit 设定
1 | ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] |
确认配置,加载配置文件,重启
1 | ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] |
再次查看 Cgroup 内存相关限制参数
1 | ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] |
生产环境,更多的是通过 drop-in
的方式定义文件
1 | ┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] |
这里我们简单看一下,其他的 Cgroup
参数限制,部分参数在 Cgroup V2 中作了调整,感兴趣小伙伴可以看看去
核心内存限制
参数 | 作用 |
---|---|
memory.limit_in_bytes |
物理内存硬限制(单位:字节),超出会触发 OOM Killer。 |
memory.memsw.limit_in_bytes |
物理内存 + swap 总限制(需内核启用 swapaccount=1 )。 |
memory.soft_limit_in_bytes |
内存软限制,系统优先回收超过此值的内存,但不会强制杀死进程。 |
内核内存控制
参数 | 作用 |
---|---|
memory.kmem.limit_in_bytes |
内核内存(如 slab、dentry 缓存)的硬限制。 |
memory.kmem.tcp.limit_in_bytes |
TCP 缓冲区内存的硬限制(如 TCP socket 发送/接收缓冲区)。 |
内存回收与行为控制
参数 | 作用 |
---|---|
memory.force_empty |
强制释放内存缓存(写入 0 触发)。 |
memory.swappiness |
调整内存回收策略(值越高,系统越积极使用 swap)。 |
memory.oom_control |
控制 OOM Killer 行为(0 表示启用 OOM Killer,1 表示禁用)。 |
memory.move_charge_at_immigrate |
进程迁移时是否转移内存占用(1 表示转移)。 |
高级功能
参数 | 作用 |
---|---|
memory.high |
内存使用软限制(v1 中较少使用,v2 中更常见)。 |
memory.low |
内存保护阈值,系统尽量避免回收低于此值的内存。 |
memory.pressure_level |
内存压力事件通知(需配合 cgroup.event_control 使用)。 |
对于这部分参数的配置,可以直接找到对应的 Cgroup
文件进行修改
1 | # 限制 TCP 缓冲区为 100MB |
ulimit
对于多用户
的系统不限制资源
本身可能就是一种不公平
, 限制系统资源比较老的方式是使用 ulimit
,由 PAM
模块在登录
和会话启动时
强制实施,ulimit
命令是bash
内置命令,主要限制了 shell
及其子进程
可用的资源
在/etc/pam.d/system-auth
文件中调用了 pam_limits
模块,此模块读取 /etc/security/limits.conf
和
/etc/security/limits.d/
,按配置文件设置资源限制。 查看模块帮助文档 man pam limits
/etc/pam.d/system-auth
是什么?
/etc/pam.d/system-auth
是一个 PAM(Pluggable Authentication Modules)
配置文件。在 Linux 系统中,PAM 提供了一种灵活的方式来配置用户认证、授权和会话管理
该文件是一个包含 PAM
配置行的文本文件,用于定义不同的认证、授权和会话模块及其参数。PAM 模块
负责处理用户登录、密码验证、权限检查
等操作。
查看文件中资源限制相关的模块,有时候我们做一些基线整改,可能需要修改该文件的相关配置
1 | ┌──[root@liruilongs.github.io]-[~] |
在PAM
配置中,pam_limits.so
模块被要求进行会话限制
PAM(Pluggable Authentication Modules)
是一个用于对用户进行认证的系统级框架。pam_limits.so
模块是 PAM 框架的一部分,它用于设置会话级别的资源限制
,例如进程可打开的文件数、进程可使用的内存
等。
ulimit
命令是用于限制用户级别资源
的工具,它通常用于控制 shell 进程
及其子进程
的资源使用。修改 ulimit 值只会对当前 shell 会话有效,对其他用户或系统进程不会产生影响(不一定)
通过 ulimit 是限制系统资源的一种途径,ulimit 支持 hard
和 soft
限制
1 | #<type> can have the two values: |
普通用户可以设置自己的软限制
,但不能高于硬限制
。可以使用 ulimit -a
查看资源限制列表
软限制 (soft maxlogins)
:软限制是一个警告阈值,当达到或超过该限制时,系统会发出警告信息,但不会阻止用户登录。硬限制 (hard maxlogins)
:硬限制是一个严格的限制,当达到或超过该限制时,系统将阻止用户登录。
1 | ┌──[root@liruilongs.github.io]-[~] |
当指定限制数
时限制,不指定时输出当前设置
通过配置文件的方式对登录次数进行限制,配置 kiosk
组 在多个终端中只能同时登录 2 次
1 | ┌──[root@liruilongs.github.io]-[~] |
涉及到内存相关的资源限制
memlock
:最大锁定内存地址空间限制(以 KB 为单位)rss
:最大常驻集大小限制(以 KB 为单位)物理内存stack
:最大堆栈大小限制(以 KB 为单位)as
:地址空间限制(以 KB 为单位)虚拟内存msgqueue
:POSIX 消息队列使用的最大内存限制(以字节为单位)
配置虚拟内存限制可以通过 ulimit
进行会话基本的虚拟内存设置,下面是一个 Demo,仅对 当前 Shell 及其子进程 生效 as 虚拟地址空间限制
,以 KB 为单位
1 | ┌──[root@liruilongs.github.io]-[~] |
修改 as 的大小之后,提示 ls
命令无法加载共享库 libc.so.6,并且无法从共享对象映射段
永久配置(全局或用户级)需要修改 /etc/security/limits.conf
, 感兴趣小伙伴可以看看我之前的博文,生效条件:用户重新登录后生效。
1 | # 格式:<domain> <type> <item> <value> |
其他的一些限制项,也可以通过上面的方式进行,比如内存锁定,最大栈,数据段等内存相关的限制
内核参数
通过内核参数
对内存的限制主要是一些缓存区的内存占用限制,以及部分 OOM 和 内存使用策略的修改,内存页分配限制策略等
这里关于怎么修改内核参数以及内核参数的加载方式,可以参考我之前的博文,这里就不再赘述了
缓存区内存限制
下面为通过关键字过滤部分的内核参数
1 | [root@developer ~]# sysctl -a | grep mem |
缓存区的话,一般用的比较多的是网络方面的,比如 TCP,UDP,Stock 等。这部分参数没有固定的值,一般根据机器使用场景动态设置
在上面的输出中,前部位为 socket
级别的网络缓存区限制,socket接受和发送数据的缓存的最大值,这里的配置往往结合 BDP
进行配置,感兴趣小伙伴可以看看我之前网络调优的博文。
1 | net.core.optmem_max = 81920 |
后部分为 TCP/UDP 级别的网络缓存区限制,TCP 缓冲区的大小应根据系统和网络的需求进行调整。较大的缓冲区可以提高网络性能,特别是在高负载或高延迟的网络环境中。但是,过大的缓冲区可能会导致内存占用增加或延迟问题。
net.ipv4.tcp_rmem 和 net.ipv4.tcp_wmem
用于配置 TCP 套接字的接收缓冲区和发送缓冲区的大小。
1 | net.ipv4.tcp_rmem = 40961310726291456 |
下面的两个为 系统级别内存限制,单位是Page 内存页,4K,分别代表了TCP和UDP的系统层面内存限制的值,即网络连接的内存分配,包括三列:min,pressure,max
.
1 | net.ipv4.tcp_mem = 78777 105039 157554 |
这里格式有点问题,我们换一个方式查看
1 | [root@developer ~]# cat /proc/sys/net/ipv4/tcp_mem |
内存超售限制
下面这组内核参数用于限制内存的超售问题
,内存的分配和使用是两个阶段,在分配的时候是虚拟内存,而且实际使用才会分配物理内存,对于虚拟内存的分配,可以是一个很大的值,但是物理内存的分配,是有限制的,如果分配的虚拟内存大于物理内存,那么就会导致内存超售,那么这个时候,就需要限制内存的超售问题,避免内存超售导致系统崩溃。
1 | [root@developer ~]# sysctl -a | grep overcomm |
vm.overcommit_memory = 0
:控制内核的内存超分配策略,决定是否允许进程申请超过物理内存 + Swap 的空间。
- 0(默认):启发式超分配(Heuristic Overcommit)。内核根据当前空闲内存、可回收缓存(PageCache/SLAB)和 Swap 综合判断是否允许分配。若申请量显著超过可用资源则拒绝。
- 1:无条件允许超分配(Always Overcommit)。来者不拒,但可能因实际内存不足触发 OOM Killer。
- 2:禁止超分配(Never Overcommit)。严格限制分配总量 ≤ (物理内存 × overcommit_ratio%) + Swap。
vm.overcommit_ratio = 50
: 当 overcommit_memory=2
时,定义物理内存的可超配比例(默认值 50%)。 计算公式: CommitLimit = (物理内存 × overcommit_ratio / 100) + Swap
vm.overcommit_kbytes = 0
:与 overcommit_ratio 互斥,直接指定超配的字节级上限(优先级高于 ratio)。
vm.nr_overcommit_hugepages = 0
:控制标准大页(HugePages)的超配数量
,允许临时分配超出 vm.nr_hugepages 预设值的大页。
上面讲到了大页,这里我们顺便看看内存页相关的内存限制
内存页限制
hugepages 用于限制分配的大页数量,这里的大页指的是标准大页
,即 2MB 的大页,
1 | sysctl -w vm.nr_hugepages=1024 # 分配1024个大页(默认2MB/页) |
如果需要自定义大页的大小,比如 1GB 的大页,需要通过 hugepagesz
来进行限制,一般启动时通过GRUB配置
1 | hugepagesz=1G hugepages=4 default_hugepagesz=1G # 分配4个1GB大页 |
关于内存页,内核相关的参数中还有透明大页
的配置,比如透明大页的开启,khugepaged
进程的扫描频率等等,感兴趣的小伙伴可以看看我之前关于大页的博文
下面为脏页/换页/内存回收
与保留相关参数的内存限制
脏页指内存中已被修改但未写入磁盘的数据页。内核通过以下参数控制其回写行为:
1 | [root@developer ~]# sysctl -a | grep dirty |
下面为交换分区使用限制
1 | vm.memcg_swap_qos_enable = 0 # 内存控制组(memcg)的Swap服务质量(QoS)功能开关 |
还有两个特殊的参数需要单独说明一下:
vm.min_free_kbytes
:系统保留的最小空闲内存(单位 KB),用于应对突发需求vm.watermark_scale_factor
:调整内存回收敏感度(默认 10,范围 1-1000)。
1 | [root@developer ~]# sysctl -a | grep min_free_kbytes |
OOM 相关内存限制
下面为OOM
相关,的内存限制,关于 OOM 是什么,什么原理不是本文重点,小伙伴可以看看我之前的博文
1 | [root@developer ~]# sysctl -a | grep oom |
NUMA内存限制
1 | [root@developer ~]# sysctl -a | grep zone_reclaim_mode |
对于内存资源的限制内核参数还有很多,这里只是列举了部分。关于Linux 内存资源限制就和小伙伴分享到这里 ^_^
博文部分内容参考
© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知 :)
《性能之巅 系统、企业与云可观测性(第2版)》
© 2018-至今 liruilonger@gmail.com, 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)
Linux 内存调优之如何限制进程、系统级别内存资源
https://liruilongs.github.io/2025/04/15/待发布/Linux 内存调优之如何限制进程、系统级别内存资源/
1.Linux网络调优之内核网络栈发包收包认知
2.Linux 性能调优之 OOM Killer 的认知与观测
3.为什么进程的物理内存占用(RSS)不停增长? 利用 BPF 跟踪、统计 Linux 缺页异常
4.如何使用 BPF 监控 Linux 用户态小内存分配:Linux 内存调优之 BPF 分析用户态小内存分配
5.Linux 内存调优之 BPF 分析用户态 mmap 大内存分配
6.如何使用 BPF 分析 Linux 内存泄漏,Linux 性能调优之 BPF 分析内核态、用户态内存泄漏
7.认识 Linux 内存构成:Linux 内存调优之页表、TLB、缺页异常、大页认知
8.Linux 系统内存监控:Linux 内存调优之系统内存全面监控